import dayjs, { Dayjs } from 'dayjs'
import { NIcon } from 'naive-ui'
import { h } from 'vue'

/**
 * Formats a given timestamp to a specified date-time format.
 *
 * @param {(Dayjs | string | number | undefined)} time - The timestamp to format.
 * @param {string} format - The format string for the output.
 * @returns {string | null} The formatted date-time string.
 */
export function formatDateTime(
  time?: Dayjs | string | number,
  format: string = 'YYYY-MM-DD HH:mm:ss'
): string | null {
  return dayjs(time).format(format)
}

/**
 * A convenience function for formatting dates without time part.
 *
 * @param {(Dayjs | string | number | undefined)} date - The date to format.
 * @param {string} format - The format string for the output.
 * @returns {string | null} The formatted date string.
 */
export function formatDate(
  date?: Dayjs | string | number,
  format: string = 'YYYY-MM-DD'
): string | null {
  return formatDateTime(date, format)
}

/**
 * Creates a throttled version of a function that will only be invoked at most once per every `wait` milliseconds.
 *
 * @param {(...args: any[]) => void} fn - The function to have throttled.
 * @param {number} wait - The number of milliseconds to throttle invocations to.
 * @returns {(...args: any[]) => void} The throttled function.
 */
export function throttle(fn: (...args: any[]) => void, wait: number): (...args: any[]) => void {
  let context: any, args: any[]
  let previous = 0

  return function (...argArr: any[]): void {
    const now = +new Date()
    context = this
    args = argArr
    if (now - previous > wait) {
      fn.apply(context, args)
      previous = now
    }
  }
}

/**
 * Creates a debounced version of a function that will delay its invocation until after `wait` milliseconds have elapsed since the last time it was invoked.
 *
 * @param {(...args: any[]) => void} method - The function to debounce.
 * @param {number} wait - The number of milliseconds to delay.
 * @param {boolean} [immediate=false] - Whether the function should be called immediately or not.
 * @returns {(...args: any[]) => void} The debounced function.
 */
export function debounce(
  method: (...args: any[]) => void,
  wait: number,
  immediate: boolean = false
): (...args: any[]) => void {
  let timeout: ReturnType<typeof setTimeout> | null = null

  return function (...args: any[]): void {
    const context = this
    if (timeout !== null) {
      clearTimeout(timeout)
    }
    if (immediate) {
      const callNow = !timeout
      timeout = setTimeout(() => {
        timeout = null
      }, wait)
      if (callNow) {
        method.apply(context, args)
      }
    } else {
      timeout = setTimeout(() => {
        method.apply(context, args)
      }, wait)
    }
  }
}

export const renderIcon = (icon: any) => () => h(NIcon, null, { default: () => h(icon) })
